/*! jQuery tinyklass traits v0.3a - Jean-Louis Grall - MIT license - http://code.google.com/p/jquery-tinyklass-plugin */

(function( $, undefined ) {

var $error = $.error,
	$inArray = $.inArray,
	$klass_orig = $.klass,	// Keep a reference to the original $.klass
	$klass = $.klass = function( _super, fields ) {
		if ( !fields ) {
			fields = _super;
			_super = undefined;
		}
		
		var _traits = fields._traits;	// Keep a reference to the traits to be added before we remove it
		
		if ( _traits ) {
			delete fields._traits;
			var _private = fields._private,
				i, trait, privateFunctions, traitFunctions, name, newMethod, destination;
			
			for ( i in _traits ) {
				trait = _traits[i];
				traitFunctions = trait.functions;
				privateFunctions = trait.privateFunctions;
				
				if ( privateFunctions && !_private ) _private = fields._private = {};
				
				for ( name in traitFunctions ) {
					destination = ( privateFunctions && privateFunctions[ name ] ) ? _private : fields;
					destination[ ".Trait." + i + "." + name ] = _createTraitMethod( traitFunctions[ name ] );
				}
			}
		}
		
		var klass = $klass_orig( _super, fields ),	// Make the new class
			proto = klass.prototype,
			traits = klass._klass.traits = [],	// This array will keep a reference to all added traits
			nameInProto,
			newMethod_klass,
			oldMethod_klass,
			conflicting;
		
		if ( _traits ) {
			for ( i in _traits ) {
				trait = _traits[i];
				traits.push(trait);
				traitFunctions = trait.functions;
				
				for ( name in traitFunctions ) {
					nameInProto = ".Trait." + i + "." + name;
					newMethod = proto[ nameInProto ];
					delete proto[ nameInProto ];
					newMethod_klass = newMethod._klass;
					newMethod_klass.name = name;
					newMethod_klass.trait = trait;
					newMethod_klass.traitFn = traitFunctions[ name ];
					
					if ( proto.hasOwnProperty( name ) ) {
						oldMethod_klass = proto[ name ]._klass;
						if ( oldMethod_klass.trait ) $error( 'Method name conflict: ' + name );
						
						conflicting = oldMethod_klass.conflicting;
						if ( !conflicting ) conflicting = oldMethod_klass.conflicting = { traits: [], methods: [] }
						conflicting.traits.push( trait );
						conflicting.methods.push( newMethod );
					}
					else proto[ name ] = newMethod;
				}
			}
		}
		
		return klass;
	},
	$proto = $klass.prototype = $klass_orig.prototype;

var $trait = $.trait = function( newFunctions ) {	// For ease of use, create the trait object. Equivalent to: return new Trait(args...)
		return new Trait( newFunctions );
	},
	Trait = $trait.Trait = $klass({	// Actually every trait is an object instantiated from this klass
		init: function( newFunctions ) {
			var traitFunctions = this.functions = {},
				_private = newFunctions._private,
				privateFunctions,
				name;
			
			if ( _private ) {
				privateFunctions = this.privateFunctions = {};
				for ( name in _private ) {
					traitFunctions[ name ] = _private[ name ];
					privateFunctions[ name ] = true;
				}
			}
			
			for ( name in newFunctions ) {
				if ( name !== "_private" ) traitFunctions[ name ] = newFunctions[ name ];
			}
		}
	}),
	// Not used for now:
	_getTraitFn = function( method ) {	// If method is a trait method, return the wrapped method. Else return the method.
		return method._klass && method._klass.traitFn ? method._klass.traitFn : method;
	},
	_createTraitMethod = function( traitFn ) {	// A trait method is a wrapper function around the original function.
		var func = function() {	// Important: the returned function must work even if called recursively on different objects
			// When the original method is called, simply change it's _klass data, such that it believes that it is a method of another klass.
			var temp = traitFn._klass;
			traitFn._klass = func._klass;
			var result = traitFn.apply( this, arguments );
			traitFn._klass = temp;
			return result;
		};
		return func;
	};

$proto._trait = function( arg1, arg2, trait ) {
	var arg1IsArguments = arg1.callee,
		method = ( arg1IsArguments ? arg1.callee : arg2.callee._klass.klass.prototype[ arg1 ] ),
		conflicting = method._klass.conflicting,
		conflictingMethod = conflicting.methods[ $inArray( (arg1IsArguments ? arg2 : trait), conflicting.traits ) ];
	return conflictingMethod.apply( this, Array.prototype.slice.call( arguments, (arg1IsArguments ? 2 : 3) ) );
}

// Like an instanceof but for traits. Let you know if an object implements a particular trait.
// Searches recursively the parent classes until it find a klass that implements the trait, and then return that klass, or undefined if not found.
$proto._hasTrait = function( trait ) {	// Returns the closest class with the trait
	var klass = this.init,
		traits;
	do traits = klass._klass.traits;
	while ( ($inArray( trait, traits ) === -1 ) && ( klass = klass._super ) );
	return klass;
};

})( jQuery );